#include "Utility.h"
#include "SharedFileName.h"
#include <algorithm>
#include <fstream>
#include <cctype>
#include <stdio.h>
#include <string>
#include <set>
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;

// file name for node sample, format (ID user_name)
const char* sample_node_file = "/sample_nodes.txt";		

// file name for sampled adjacency list, directed graph for twitter
// format:
// first line: number of sampled users
// second line onward: ID number_of_neighbours neihbour IDs
const char* sample_adjlist_file = "/sample_adjlist.txt";  

// weigthed graph of the sample graph
const char* sample_weighted_adjlist_file="/sample_weighted_adjlist.txt";

// partition to partition information and paritition pivot 
const char* sample_partition_dist_and_pivot="/sample_partition_pivot.txt";

// sample the tweets which are published by the sampled users
const char* sample_filtered_data = "/sample_filtered_data.txt";

// sampled keyword file with the keyword IDF (inverse docuement frequency)
const char* sample_keyword_file = "/sample_keywords.txt";

// transformed filtered_data which the tweets are represented as a vector of keyword IDs with their TF (term frequency)
const char* sample_transformed_data = "/sample_transformed_data.txt";

// inverval file
const char* sample_freq_interval = "/sample_frequency_interval.txt";

// modified node map to tree partition
const char* sample_tree_partition_node_map = "/sample_tree_partition_node_map.txt";

const char* sample_tree_partition_dist = "/sample_tree_partition_dist.txt";

// nearest neighbor distance for each node
const char* sample_node2hop_nn = "/sample_node2hop_nn.txt";

// all related with DB name
const char* sample_database_name = "/test.db";
const char* doc_table_name = "docTable";
const char* doc_term_table_name = "docTermTable";

// schema name
const char* doc_id_name = "docID";
const char* time_name = "time";
const char* time_partition_name = "partition";
const char* person_id_name = "person";
const char* term_id_name = "termID";
const char* term_freq_name = "term_freq";


bool IsStringContainNonAsciiChar(string& s){
	for(string::iterator it = s.begin(); it != s.end(); it++){
		if(!isprint((unsigned)(*it))) return true;
	}
	return false;
}

string ReplaceStrangeCharacterWithSpace(string& str){
	string result;
	for(string::iterator it = str.begin(); it != str.end(); ++it){
		char c = *it;

		// special character should stay for @ and # and other will replace by space
		if(!isalpha(c) && !isdigit(c) && c != '@' && c != '#') c = ' ';
		else if(isalpha(c)) c = tolower(c);

		result.append(1,c);
	}
	return result;
}

bool IsTokenKeyword(string& token, set<string>& stop_word_set){
	char first_char = token.at(0);
	if(first_char == '@') return false;

	int repeat = 1;
	char c = '0';
	for(string::iterator it = token.begin(); it != token.end(); it++){
		if(*it == c) repeat++;
		else{
			repeat = 1;
			c = *it;
		}
		if(repeat >= 3) return false;
		if(isdigit(*it)) return false;
	}

	if(first_char == '#'){
		while(token.length() > 1){
			char second_char = token.at(1);
			if(isalpha(second_char) || isdigit(second_char)) return true;
			else if(second_char == '#'){
				token.erase(0,1);
			}else{
				break;
			}
		}
		return false;
	}

	

	if(token.length() == 1) return false;

	if(IsTokenAllDigit(token)) return false;

	

	return stop_word_set.find(token) == stop_word_set.end();
}

bool IsTokenAllDigit(string& token){
	for(string::iterator it = token.begin(); it != token.end(); it++)
		if(!isdigit(*it)) return false; 
	return true;
}

void LoadAdjGraph(vector<vector<pair<int,int> > >& adj_lists, string in_dir){
	string adj_file = in_dir;
	ifstream graphAdj(adj_file.append(sample_weighted_adjlist_file).c_str());
	if(!graphAdj.is_open()){
		cout << "canont open graph weighted adjlist" << endl;
		exit(1);
	}

	string line;
	getline(graphAdj,line); // skip the first line

	stringstream first_ss(line);
	int num_nodes = 0;
	first_ss >> num_nodes;
	adj_lists.resize(num_nodes);

	int count = 0;
	int num_edges = 0;
	while(getline(graphAdj,line)){
		stringstream ss(line);
		while(true){
			int node_id,weight;
			if(!(ss >> node_id)) break;
			if(!(ss >> weight)){
				cout << "file format is wrong" << endl;
				exit(1);
			}
			node_id--;
			weight = 100 - weight; // spcial arrangement
			adj_lists[count].push_back(make_pair(node_id,weight));
			num_edges++;
		}
		count++;
	}
	graphAdj.close();
	cout << "finish reading graph weight adj file" << endl;
	cout << "number of edges: " << (num_edges/2) << endl;
}

void Load2HopNN(vector<int>& nn2hop, string in_dir){
	ifstream nn2hopFile(in_dir.append(sample_node2hop_nn));
	if(!nn2hopFile.is_open()){
		cout << "canont graph partiion file" << endl;
		exit(1);
	}

	string line;
	while(nn2hopFile >> line){
		nn2hop.push_back(atoi(line.c_str()));
	}
	nn2hopFile.close();
}

int LoadGraphPartition(unordered_map<int,int>& node_partition_map,string in_dir, int num_partition){

	// load user to partition map
	string partition_file = in_dir;
	partition_file.append(sample_weighted_adjlist_file);
	partition_file.append(".part.");
	ostringstream convert;
	convert << num_partition;
	partition_file.append(convert.str());
	cout << "partition file: " << partition_file << endl;

	ifstream partFile(partition_file.c_str());
	if(!partFile.is_open()){
		cout << "canont graph partiion file" << endl;
		exit(1);
	}

	set<int> dist_partition;
	vector<vector<int> > partition_map(num_partition);

	string line;
	int count = 0;
	while(partFile >> line){
		int partition_id = atoi(line.c_str());
		dist_partition.insert(partition_id);
		partition_map[partition_id].push_back(count);
		count++;
	}
	partFile.close();

	int partition_count = -1;
	for(int i = 0; i < num_partition; i++){
		if(partition_map[i].size() != 0) partition_count++;
		for(int j = 0; j < partition_map[i].size(); j++){
			node_partition_map[partition_map[i][j]] = partition_count;
		}
	}
	
	cout << "number of unique partition: " << dist_partition.size() << endl;
	cout << "finish reading partition file with " << count << " user" << endl;

	return dist_partition.size();
}

void LoadPartitionDistAndPivots(
	vector<vector<int> >& partition_dist,
	vector<int>& pivot_nodes,
	vector<vector<int> >& pivot_to_all_dist,	
	string in_dir, int num_nodes, int num_partition){

	// load partition to partition distance
	string dist_file = in_dir;
	dist_file.append(sample_partition_dist_and_pivot);
	ifstream partDistFile(dist_file.c_str());

	string line;
	getline(partDistFile,line);
	if(atoi(line.c_str()) != num_partition){
		cout << "partition file has problem, number of partition does not match" << endl;
		exit(1);
	}

	partition_dist.resize(num_partition);
	for(int i = 0; i < num_partition; i++){
		string dist_line;
		getline(partDistFile,dist_line);
		stringstream ss(dist_line);
		
		partition_dist[i].resize(num_partition);
		for(int j = 0; j < num_partition; j++){
			ss >> partition_dist[i][j];
		}
	}

	pivot_nodes.resize(num_partition);
	pivot_to_all_dist.resize(num_partition);
	for(int i = 0; i < num_partition; i++){
		partDistFile >> pivot_nodes[i]; // load pivot node
		pivot_to_all_dist[i].resize(num_nodes);

		for(int j = 0; j < num_nodes; j++){
			partDistFile >> pivot_to_all_dist[i][j];
		}
	}

	partDistFile.close();
	cout << "finished reading partition distance file" << endl;
}

void LoadKeyword(unordered_map<string,int>& keyword_map,vector<double>& keyword_idf,string in_dir){
	ifstream keywordFile(in_dir.append(sample_keyword_file).c_str());
	if(!keywordFile.is_open()){
		cout << "cannot open keyword file: " << in_dir << endl;
		exit(1);
	}
	string line;
	int max_count = 0;
	int max_count_keywords = 0;
	int num_keywords = 0;

	while(getline(keywordFile,line)){
		stringstream ss(line);
		
		// set the map relationship
		string keyword;
		ss >> keyword;
		keyword_map[keyword] = num_keywords;
	

		// set idf
		int count;
		ss >> count;
		if(max_count < count){
			max_count = count;
			max_count_keywords = num_keywords;
		}
		num_keywords++;
		double idf = (double)1.0/count; // because we need ln(1+|R|/idf)
		keyword_idf.push_back(idf);
	}
	keywordFile.close();
	cout << "finish reading the keyword file with max count :" << max_count << " with max keyword: " << max_count_keywords << endl;
}

void SQLerror(int rc, char* zErrMsg, bool print){
	if( rc != SQLITE_OK ){
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
		sqlite3_free(zErrMsg);
		exit(1);
	}
	if(print){
		fprintf(stdout, "sql executed sucessfully\n");
	}
}